package com.book.oauth.server.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.book.common.api.entity.CurrentUser;
import com.book.common.api.oauth.CacheKey;
import com.book.common.codec.Md5Utils;
import com.book.common.lang.Tuple2;
import com.book.common.result.Failed;
import com.book.common.result.RestResponse;
import com.book.common.result.Success;
import com.book.common.util.GuavaCacheUtil;
import com.book.common.util.RegularUtil;
import com.book.oauth.server.company.entity.Company;
import com.book.oauth.server.company.service.ICompanyService;
import com.book.oauth.server.dept.entity.Dept;
import com.book.oauth.server.dept.service.IDeptService;
import com.book.oauth.server.login.log.entity.LoginLog;
import com.book.oauth.server.login.log.service.ILoginLogService;
import com.book.oauth.server.role.entity.Role;
import com.book.oauth.server.role.service.IRoleService;
import com.book.oauth.server.route.entity.RoleRoute;
import com.book.oauth.server.route.entity.Route;
import com.book.oauth.server.route.service.IRoleRouteService;
import com.book.oauth.server.route.service.IRouteService;
import com.book.oauth.server.user.dto.UserDto;
import com.book.oauth.server.user.entity.User;
import com.book.oauth.server.user.entity.UserRole;
import com.book.oauth.server.user.mapper.UserMapper;
import com.book.oauth.server.user.service.IUserRoleService;
import com.book.oauth.server.user.service.IUserService;
import com.book.modules.core.service.impl.BaseServiceImpl;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Description：用户 ServiceImpl
 *
 * @Author： leo.xiong
 * @CreateDate： 2020-04-26
 * @Email： leo.xiong@suyun360.com
 * @Version:
 */
@Service
@DS(value = "oauth")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IUserRoleService userRoleService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private IDeptService deptService;

    @Autowired
    private IRoleService roleService;

    @Autowired
    private IRouteService routeService;

    @Autowired
    private IRoleRouteService roleRouteService;

    @Autowired
    private ILoginLogService loginLogService;

    @Autowired
    private ResourceServerTokenServices resourceServerTokenServices;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(User user) {
        user.setPassword(Md5Utils.md5(user.getPassword()));
        user.setSex(UserDto.SEX_UNKNOW);
        user.setAvatar(UserDto.DEFAULT_AVATAR);
        user.setTheme(UserDto.THEME_BLACK);
        user.setIsTab(UserDto.TAB_OPEN);
        user.setDescription("注册用户");
        super.save(user);
        UserRole ur = new UserRole();
        ur.setUserId(user.getId());
        ur.setRoleId(2L);
        this.userRoleService.save(ur);
        return true;
    }

    /**
     * 根据token获取用户信息
     *
     * @param token
     * @return
     */
    @Override
    public RestResponse<CurrentUser> getCurrentUserByToken(String token) {
        CurrentUser authPrincipal = GuavaCacheUtil.get(CacheKey.PUBLIC_USER_TOKEN + token, CurrentUser.class);
        if (authPrincipal != null) {
            return new Success<>(authPrincipal);
        }
        OAuth2Authentication oAuth2Authentication = null;
        try {
            oAuth2Authentication = resourceServerTokenServices.loadAuthentication(token);
        } catch (AuthenticationException e) {
            return new Failed("BOOK:CORE:10009", "BOOK:CORE:10009");
        } catch (InvalidTokenException e) {
            return new Failed("BOOK:CORE:10008", "BOOK:CORE:10008");
        }
        if (oAuth2Authentication == null || oAuth2Authentication.getPrincipal() == null) {
            return new Failed("BOOK:CORE:10008", "BOOK:CORE:10008");
        }
        CurrentUser currentUser = JSONObject.parseObject(JSONObject.toJSONString(oAuth2Authentication.getPrincipal()), CurrentUser.class);
        if (currentUser == null) {
            return new Failed("BOOK:COMMON:10029", "");
        }
        GuavaCacheUtil.put(CacheKey.PUBLIC_USER_TOKEN + token, currentUser);
        return new Success<>(currentUser);
    }

    @Override
    public CurrentUser getCurrentUser(String token) {
        RestResponse<CurrentUser> restResponse = getCurrentUserByToken(token);
        if (restResponse.isFailed()) {
            return null;
        } else {
            return restResponse.getData();
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLoginTime(String userId, String userName, String location, String ip, String browser, String system) {
        Date loginTime = new Date();
        if (RegularUtil.isNum(userId)) {
            User user = new User();
            user.setLastLoginTime(loginTime);
            super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, Long.valueOf(userId)));
        }
        LoginLog loginLog = new LoginLog();
        loginLog.setUsername(userName);
        loginLog.setUserId(userId);
        loginLog.setLoginTime(loginTime);
        loginLog.setBrowser(browser);
        loginLog.setIp(ip);
        loginLog.setLocation(location);
        loginLog.setSystemInfo(system);
        loginLogService.save(loginLog);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(Long id) {
        User user = new User();
        user.setPassword(Md5Utils.md5(UserDto.DEFAULT_PASSWORD));
        super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RestResponse updatePassword(Long id, String oldPassword, String password) {
        User user = super.getById(id);
        if (user == null) {
            return new Failed("BOOK:OAUTH:10000", "");
        }
        if (!user.getPassword().equals(passwordEncoder.encode(oldPassword))) {
            return new Failed("BOOK:OAUTH:10001", "");
        }
        user.setPassword(passwordEncoder.encode(password));
        super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, id));
        return new Success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAvatar(Long id, String avatar) {
        User user = new User();
        user.setAvatar(avatar);
        super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTheme(Long id, String theme) {
        User user = new User();
        user.setTheme(theme);
        super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTab(Long id, String isTab) {
        User user = new User();
        user.setIsTab(isTab);
        super.update(user, new LambdaQueryWrapper<User>().eq(User::getId, id));
    }

    /**
     * 拼装当前用户信息，并返回当前用户的路由信息
     *
     * @param currentUser
     * @return
     */
    @Override
    public Tuple2<CurrentUser, List<Route>> buildCurrentUserInfo(CurrentUser currentUser) {
        if (currentUser.getCompanyId() != null) {
            Company company = companyService.getById(currentUser.getCompanyId());
            if (company != null) {
                currentUser.setCompanyName(company.getCompanyName());
            }
        }
        if (currentUser.getDeptId() != null) {
            Dept dept = deptService.getById(currentUser.getDeptId());
            if (dept != null) {
                currentUser.setDeptName(dept.getDeptName());
            }
        }
        if (currentUser.getRoleId() != null) {
            Role role = roleService.getById(currentUser.getRoleId());
            if (role == null) {
                return new Tuple2<>(currentUser, Lists.newArrayList());
            }
            currentUser.setRoleName(role.getRoleName());
            List<RoleRoute> roleRouteList = roleRouteService.findAllList(new LambdaQueryWrapper<RoleRoute>().eq(RoleRoute::getRoleId, currentUser.getRoleId()));
            if (CollectionUtils.isEmpty(roleRouteList)) {
                return new Tuple2<>(currentUser, Lists.newArrayList());
            }
            List<Long> routeIdList = roleRouteList.parallelStream().map(RoleRoute::getRouteId).collect(Collectors.toList());
            List<Route> routeList = routeService.findAllList(new LambdaQueryWrapper<Route>().in(Route::getId, routeIdList));
            if (CollectionUtils.isEmpty(routeList)) {
                return new Tuple2<>(currentUser, Lists.newArrayList());
            }
            for (Route route : routeList) {
                if (StringUtils.isNotEmpty(route.getPermission())) {
                    currentUser.getPermissionList().add(route.getPermission());
                }
            }
            return new Tuple2<>(currentUser, routeList);
        }
        return new Tuple2<>(currentUser, Lists.newArrayList());
    }
}
